package tester

import (
	"fmt"
	"regexp"
	"strings"
)

// varName value;        prefix match
// varName == value;     euqal match
// varName != value;     unequal match
// varName ~ value;      regex match
const (
	prefix_match int = iota
	equal_match
	unequal_match
	regex_match
)

const (
	skey int = iota
	skey_end
	styp
	styp_end
	sval
)

type cond struct {
	varName string
	typ     int
	value   string

	reg *regexp.Regexp
}

func (c *cond) match(vars map[string]string) bool {
	val, ok := vars[c.varName]

	if ok {
		switch c.typ {
		case prefix_match:
			if strings.HasPrefix(val, c.value) {
				return true
			}
		case equal_match:
			if val == c.value {
				return true
			}
		case unequal_match:
			if val != c.value {
				return true
			}
		case regex_match:
			if c.reg.Match([]byte(val)) {
				return true
			}
		}
	}

	return false
}

func matchCondSymbol(b byte) bool {
	if b == '=' || b == '!' || b == '~' {
		return true
	}

	return false
}

func parseCond(expr string) (*cond, error) {
	c := &cond{}

	b := []byte(expr)
	lb := len(b)
	start := 0
	stat := skey

	i := 0
	for ; i < lb; i++ {
		switch stat {
		case skey:
			if isSpace(b[i]) {
				c.varName = string(b[start:i])
				stat = skey_end

				continue
			}

			if matchCondSymbol(b[i]) {
				c.varName = string(b[start:i])
				stat = styp
				start = i

				continue
			}

		case skey_end:
			if isSpace(b[i]) {
				continue
			}

			if matchCondSymbol(b[i]) {
				stat = styp
			} else {
				stat = sval
				c.typ = prefix_match
			}

			start = i

		case styp:
			if b[start] == '=' && b[i] == '=' {
				c.typ = equal_match
				start = i + 1

				if start == lb {
					return nil, fmt.Errorf("parse equal_match error, expr(%s) index is %d, state is %d", expr, i, stat)
				}

				if isSpace(b[start]) {
					stat = styp_end
				} else {
					stat = sval
				}

				continue
			}

			if b[start] == '!' && b[i] == '=' {
				c.typ = unequal_match
				start = i + 1

				if start == lb {
					return nil, fmt.Errorf("parse unequal_match error, expr(%s) index is %d, state is %d", expr, i, stat)
				}

				if isSpace(b[start]) {
					stat = styp_end
				} else {
					stat = sval
				}

				continue
			}

			if b[start] == '~' {
				c.typ = regex_match
				start = i
				i--

				if isSpace(b[start]) {
					stat = styp_end
				} else {
					stat = sval
				}

				continue
			}

			return nil, fmt.Errorf("parse unknown match type error, expr(%s) index is %d, state is %d", expr, i, stat)

		case styp_end:
			if isSpace(b[i]) {
				continue
			}

			start = i
			i--
			stat = sval

		case sval:
			c.value = string(b[start:])
			if c.typ == regex_match {
				var err error
				c.reg, err = regexp.Compile(c.value)
				if err != nil {
					return nil, err
				}
			}

			return c, nil
		}
	}

	return nil, fmt.Errorf("parse error, expr(%s) index is %d, state is %d", expr, i, stat)
}
